﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZedGraph;
using System.Drawing;

using System.Windows.Forms;
using System.ComponentModel;
using System.Data;
using System.Drawing;

using Bestcode.MathParser;

namespace NumMethods_HW1_Hydra
{
    class Grid
    {
        private double TPeriod;
        private int TNumParts;
        private double stepTao;

        private double xLeftCoordinate;
        private double xRightCoordinate;       
        private int XNumParts;
        private double stepH;

        private int numDumpTimes;
        private double[,] U1_Evolution;
        private double[,] U2_Evolution;

        private double[] xCoordinates;

        // ===== Params =====
        private double lambda_1;
        private double ro_1;
        private double k_1;
        private double gamma_1;

        private double lambda_2;
        private double c_2;
        private double nv_2;

        // ===== Вспомогательные массивы =====
        private double[] U1_new;
        private double[] U2_new;

        private double[] U1_old;
        private double[] U2_old;

        // ===== Parser =====
        MathParser parser;

        public Grid(int TNumParts, double stepTao, double xLeftCoordinate, double xRightCoordinate, int XNumParts, int numDumpTimes)
        {
            this.TPeriod = stepTao * TNumParts;
            this.TNumParts = TNumParts;
            this.stepTao = stepTao;

            this.xLeftCoordinate = xLeftCoordinate;
            this.xRightCoordinate = xRightCoordinate;
            this.XNumParts = XNumParts;
            this.stepH = (xRightCoordinate - xLeftCoordinate) / XNumParts;

            this.numDumpTimes = numDumpTimes;

            U1_Evolution = new double[XNumParts + 1, numDumpTimes + 1]; // Начинаем с НУ - поэтому +1
            U2_Evolution = new double[XNumParts + 1, numDumpTimes + 1]; // Начинаем с НУ - поэтому +1

            xCoordinates = new double[XNumParts + 1];

            for (int ix = 0; ix <= XNumParts; ix++)
                xCoordinates[ix] = xLeftCoordinate + ix * stepH;

            U1_new = new double[XNumParts + 1];
            U2_new = new double[XNumParts + 1];

            U1_old = new double[XNumParts + 1];
            U2_old = new double[XNumParts + 1];

        }

        public void Calculation()
        {
            int currDump = 0;

            for (int it = 0; it <= (TNumParts - 1); it++)
            {
                for (int ix = 1; ix <= (XNumParts - 1); ix++)
                {
                    U1_new[ix] = U1_old[ix] + stepTao * (lambda_1 * (U1_old[ix - 1] - 2.0 * U1_old[ix] + U1_old[ix + 1]) / Math.Pow(stepH, 2) + ro_1 + k_1 * Math.Pow(U1_old[ix], 2) / U2_old[ix] - gamma_1 * U1_old[ix]);
                    U2_new[ix] = U2_old[ix] + stepTao * (lambda_2 * (U2_old[ix - 1] - 2.0 * U2_old[ix] + U2_old[ix + 1]) / Math.Pow(stepH, 2) + c_2 * Math.Pow(U1_old[ix], 2) - nv_2 * U2_old[ix]);
                }

                // ===== Boundary Conditions for left border =====
                U1_new[0] = U1_new[1];
                U2_new[0] = U2_new[1];

                // ===== Boundary Conditions for right border =====
                U1_new[XNumParts] = U1_new[XNumParts - 1];
                U2_new[XNumParts] = U2_new[XNumParts - 1];
              
                if (it % (TNumParts / numDumpTimes) == 0)
                {
                    for (int ix = 0; ix <= XNumParts; ix++)
                    {
                        U1_Evolution[ix, currDump] = U1_old[ix];
                        U2_Evolution[ix, currDump] = U2_old[ix];
                    }

                    currDump++;
                }

                // ========= Copy for next step ============
                for (int ix = 0; ix <= XNumParts; ix++)
                {
                    U1_old[ix] = U1_new[ix];
                    U2_old[ix] = U2_new[ix];
                }
            }
            
            // ==== Запись самого последнего значения ====

            for (int ix = 0; ix <= XNumParts; ix++)
            {
                U1_Evolution[ix, currDump] = U1_old[ix];
                U2_Evolution[ix, currDump] = U2_old[ix];
            }
        }

        public void initParams(double lambda_1, double ro_1, double k_1, double gamma_1, double lambda_2, double c_2, double nv_2)
        {
            this.lambda_1 = lambda_1;
            this.ro_1 = ro_1;
            this.k_1 = k_1;
            this.gamma_1 = gamma_1;

            this.lambda_2 = lambda_2;
            this.c_2 = c_2;
            this.nv_2 = nv_2;

            // Initial Conditions
            for (int ix = 0; ix <= XNumParts; ix++)
            {
                U1_old[ix] = initConditions_U1(xCoordinates[ix]);
                U2_old[ix] = initConditions_U2(xCoordinates[ix]);
            }
        }


        public void initParamsParser(double lambda_1, double ro_1, double k_1, double gamma_1, double lambda_2, double c_2, double nv_2, TextBox u1, TextBox u2)
        {
            this.lambda_1 = lambda_1;
            this.ro_1 = ro_1;
            this.k_1 = k_1;
            this.gamma_1 = gamma_1;

            this.lambda_2 = lambda_2;
            this.c_2 = c_2;
            this.nv_2 = nv_2;

            // Initial Conditions
            for (int ix = 0; ix <= XNumParts; ix++)
            {
                U1_old[ix] = initConditions_U1_Parser(xCoordinates[ix],u1);
                U2_old[ix] = initConditions_U2_Parser(xCoordinates[ix],u2);
            }
        }

        public double uniform_condition_U1()
        {
            return (ro_1 * c_2 + k_1 * nv_2) / (c_2 * gamma_1);
        }

        public double uniform_condition_U2()
        {
            return c_2 * Math.Pow(ro_1 * c_2 + k_1 * nv_2, 2) / nv_2;
        }

        private double initConditions_U1(double x_value)
        {
            return uniform_condition_U1() + 1.0 * Math.Cos(3.0 * Math.PI * x_value);
        }

        private double initConditions_U2(double x_value)
        {
            return uniform_condition_U2() + 1.0 * Math.Cos(2.0 * Math.PI * x_value);
        }

        public void initParser()
        {
            parser = new MathParser();
        }

        private double initConditions_U1_Parser(double x_value, TextBox tb)
        {
            parser.Expression = tb.Text;
            parser.X = x_value;
            double parserResult = parser.ValueAsDouble;

            return uniform_condition_U1() + parserResult;
        }

        private double initConditions_U2_Parser(double x_value, TextBox tb)
        {
            parser.Expression = tb.Text;
            parser.X = x_value;
            double parserResult = parser.ValueAsDouble;

            return uniform_condition_U1() + parserResult;
        }

        public void DrawGraphics(ZedGraphControl zedGraph_U1, ZedGraphControl zedGraph_U2, int time)
        {
            GraphPane pane_U1 = zedGraph_U1.GraphPane;
            GraphPane pane_U2 = zedGraph_U2.GraphPane;

            pane_U1.Title.Text = "U1";
            pane_U2.Title.Text = "U2";

            // Изменим текст надписи по оси Х
            pane_U1.XAxis.Title.Text = "X";
            // Изменим текст по оси Y
            pane_U1.YAxis.Title.Text = "U1(x,t)";

            // Изменим текст надписи по оси Х
            pane_U2.XAxis.Title.Text = "X";
            // Изменим текст по оси Y
            pane_U2.YAxis.Title.Text = "U2(x,t)";

            pane_U1.CurveList.Clear();
            pane_U2.CurveList.Clear();

            PointPairList list_U1 = new PointPairList();
            PointPairList list_U2 = new PointPairList();

            for (int ix = 0; ix <= XNumParts; ix++)
            {
                list_U1.Add(xCoordinates[ix], U1_Evolution[ix, time]);
                list_U2.Add(xCoordinates[ix], U2_Evolution[ix, time]);
            }

            LineItem curve_u1 = pane_U1.AddCurve("U1", list_U1, Color.Blue, SymbolType.None);
            LineItem curve_u2 = pane_U2.AddCurve("U2", list_U2, Color.Red, SymbolType.None);

            // Включаем отображение сетки напротив крупных рисок по оси X
            pane_U1.XAxis.MajorGrid.IsVisible = true;
            pane_U2.XAxis.MajorGrid.IsVisible = true;

            // Задаем вид пунктирной линии для крупных рисок по оси X:
            // Длина штрихов равна 10 пикселям, ... 
            pane_U1.XAxis.MajorGrid.DashOn = 10;
            pane_U2.XAxis.MajorGrid.DashOn = 10;

            // затем 5 пикселей - пропуск
            pane_U1.XAxis.MajorGrid.DashOff = 5;
            pane_U2.XAxis.MajorGrid.DashOff = 5;


            // Включаем отображение сетки напротив крупных рисок по оси Y
            pane_U1.YAxis.MajorGrid.IsVisible = true;
            pane_U2.YAxis.MajorGrid.IsVisible = true;

            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane_U1.YAxis.MajorGrid.DashOn = 10;
            pane_U1.YAxis.MajorGrid.DashOff = 5;
            pane_U2.YAxis.MajorGrid.DashOn = 10;
            pane_U2.YAxis.MajorGrid.DashOff = 5;

            // Включаем отображение сетки напротив мелких рисок по оси X
            pane_U1.YAxis.MinorGrid.IsVisible = true;
            pane_U2.YAxis.MinorGrid.IsVisible = true;

            // Задаем вид пунктирной линии для крупных рисок по оси Y: 
            // Длина штрихов равна одному пикселю, ... 
            pane_U1.YAxis.MinorGrid.DashOn = 1;
            pane_U2.YAxis.MinorGrid.DashOn = 1;

            // затем 2 пикселя - пропуск
            pane_U1.YAxis.MinorGrid.DashOff = 2;
            pane_U2.YAxis.MinorGrid.DashOff = 2;

            // Включаем отображение сетки напротив мелких рисок по оси Y
            pane_U1.XAxis.MinorGrid.IsVisible = true;
            pane_U2.XAxis.MinorGrid.IsVisible = true;

            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane_U1.XAxis.MinorGrid.DashOn = 1;
            pane_U1.XAxis.MinorGrid.DashOff = 2;
            pane_U2.XAxis.MinorGrid.DashOn = 1;
            pane_U2.XAxis.MinorGrid.DashOff = 2;


            zedGraph_U1.AxisChange();
            zedGraph_U1.Invalidate();

            zedGraph_U2.AxisChange();
            zedGraph_U2.Invalidate();
        }

        public void DrawComparativeGraphic(ZedGraphControl zedGraph, int time)
        {
            GraphPane pane = zedGraph.GraphPane;

            // Изменим текст надписи по оси Х
            pane.XAxis.Title.Text = "X";
            // Изменим текст по оси Y
            pane.YAxis.Title.Text = "U1(x,t), U2(x,t)";

            pane.Title.Text = "U1(x,t) & U2(x,t)";

            pane.CurveList.Clear();

            PointPairList list_U1 = new PointPairList();
            PointPairList list_U2 = new PointPairList();
            PointPairList listZero = new PointPairList();

            for (int ix = 0; ix <= XNumParts; ix++)
            {
                list_U1.Add(xCoordinates[ix], U1_Evolution[ix, time]);
                list_U2.Add(xCoordinates[ix], U2_Evolution[ix, time]);
                listZero.Add(xCoordinates[ix], 0.0);
            }

            LineItem curve_u1 = pane.AddCurve("U1", list_U1, Color.Blue, SymbolType.None);
            LineItem curve_u2 = pane.AddCurve("U2", list_U2, Color.Red, SymbolType.None);
            LineItem curve_zero = pane.AddCurve("", listZero, Color.Black, SymbolType.None);

            // Включаем отображение сетки напротив крупных рисок по оси X
            pane.XAxis.MajorGrid.IsVisible = true;

            // Задаем вид пунктирной линии для крупных рисок по оси X:
            // Длина штрихов равна 10 пикселям, ... 
            pane.XAxis.MajorGrid.DashOn = 10;

            // затем 5 пикселей - пропуск
            pane.XAxis.MajorGrid.DashOff = 5;


            // Включаем отображение сетки напротив крупных рисок по оси Y
            pane.YAxis.MajorGrid.IsVisible = true;

            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.YAxis.MajorGrid.DashOn = 10;
            pane.YAxis.MajorGrid.DashOff = 5;

            // Включаем отображение сетки напротив мелких рисок по оси X
            pane.YAxis.MinorGrid.IsVisible = true;

            // Задаем вид пунктирной линии для крупных рисок по оси Y: 
            // Длина штрихов равна одному пикселю, ... 
            pane.YAxis.MinorGrid.DashOn = 1;

            // затем 2 пикселя - пропуск
            pane.YAxis.MinorGrid.DashOff = 2;

            // Включаем отображение сетки напротив мелких рисок по оси Y
            pane.XAxis.MinorGrid.IsVisible = true;

            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.XAxis.MinorGrid.DashOn = 1;
            pane.XAxis.MinorGrid.DashOff = 2;

            zedGraph.AxisChange();
            zedGraph.Invalidate();

        }
    }
}
